Title Banner

Previous Book Contents Book Index Next

Inside Macintosh: QuickTime Components /
Chapter 9 - Movie Data Exchange Components


Creating a Movie Data Exchange Component

This section discusses the details of creating a movie data exchange component. This section includes source code for two simple movie data exchange components.

You should consider creating a movie data import component if you have data that you would like to place in a QuickTime movie and there are not currently facilities for placing that type of data into a movie. Similarly, if you want to work with data from a QuickTime movie without using QuickTime, you might consider creating a movie data export component that can convert the data into a format your program can understand.

After reading this section, you should understand all of the special requirements of these components. The functional interface that your component must support is described in "Movie Data Exchange Components Reference" beginning on page 9-20. Note that a single component may support only import or export functions, not both.

Before reading this section, you should be familiar with how to create components. See the chapter "Component Manager" in Inside Macintosh: More Macintosh Toolbox for a complete discussion of components, how to use them, and how to create them.

Apple has defined component type values for movie data exchange components. You can use the following constants to specify this component type:

#define MovieImportType 'eat '      /* movie data import */
#define MovieExportType 'spit'      /* movie data export */
Apple has defined a functional interface for movie data exchange components. For information about the functions that your component must support, see "Movie Data Exchange Components Reference" beginning on page 9-20. You can use the following constants to refer to the request codes for each of the functions that your component must support:

enum {
   /* movie data import components */
   kMovieImportHandleSelect            = 1,  /* import from handle */
   kMovieImportFileSelect              = 2,  /* import from file */
   kMovieImportSetSampleDurationSelect = 3,  /* set sample duration */
   kMovieImportSetSampleDescriptionSelect 
                                       = 4,  /* set sample description */
   kMovieImportSetMediaFileSelect      = 5,  /* set media file */
   kMovieImportSetDimensionsSelect     = 6,  /* set track dimensions */
   kMovieImportSetChunkSizeSelect      = 7,  /* set chunk size */
   kMovieImportSetProgressProcSelect   = 8,  /* set progress function */
   kMovieImportSetAuxiliaryDataSelect  = 9,  /* set additional data */
   kMovieImportSetFromScrapSelect      = 10, /* data from scrap */
   kMovieImportDoUserDialogSelect      = 11, /* invoke user dialog box */
   kMovieImportSetDurationSelect       = 12  /* set paste duration */
   /* movie data export components */
   kMovieExportToHandleSelect          = 128,/* export to handle */
   kMovieExportToFileSelect            = 129,/* export to file */
   kMovieExportDoUserDialogSelect      = 130,/* invoke user dialog box */
   kMovieExportGetAuxiliaryDataSelect  = 131,/* get additional data */
   kMovieExportSetProgressProcSelect ` = 132 /* set progress function */
};

A Sample Movie Import Component

This section describes how to create a movie import component. First you implement the required functions. Then you instruct your component to obtain the movie data from a handle or a file. This section then supplies a sample program that implements a movie data exchange component that imports a Scrapbook file containing QuickDraw PICT images. (For details on QuickDraw PICT images, see the chapter "Basic QuickDraw" in Inside Macintosh: Imaging.)

Your movie data import component may provide a user dialog box. You may use this dialog box in any way that is appropriate for your component--for example, to
obtain certain parameter information governing the import operation, such as the
image-compression method.

In addition, the requesting application may use one or more of the configuration functions to establish parameters for the import operation.

You should not rely on any outside configuration information. Your component should work properly knowing only the source data and the target movie. The Movie Toolbox supplies this information to your component when it calls your MovieImportHandle function (described on page 9-21) or MovieImportFile function (described on page 9-24).

Your movie data import component may implement either one or both of these functions, which allow the Movie Toolbox to request that data be converted into a format for use in a QuickTime movie.

Set the appropriate flags in your component's componentFlags field to indicate which of these functions your component supports. Note that your component may support both functions.

Implementing the Required Import Component Functions

Listing 9-1 supplies a sample program that implements a movie data exchange component that imports a Scrapbook file containing QuickDraw PICT images. (For details on QuickDraw PICT images, see the chapter "Basic QuickDraw" in Inside Macintosh: Imaging.) The sample program also provides the dispatchers for the movie import component together with the required functions.

Listing 9-1 Implementing the required import functions

#define kMediaTimeScale 600
typedef struct {
   ComponentInstance self
   TimeValue         frameDuration;
} ImportScrapbookGlobalsRecord, **ImportScrapbookGlobals;
/* entry point for all Component Manager requests */
pascal ComponentResult ImportScrapbookDispatcher
                               (ComponentParameters *params,
                                  Handle storage)
{
   OSErr err = badComponentSelector;
   ComponentFunction componentProc = 0;
   switch (params->what) {
      case kComponentOpenSelect: 
         componentProc = ImportScrapbookOpen; break;
      case kComponentCloseSelect: 
         componentProc = ImportScrapbookClose; break;
      case kComponentCanDoSelect: 
         componentProc = ImportScrapbookCanDo; break;
      case kComponentVersionSelect: 
         componentProc = ImportScrapbookVersion; break;
      case kMovieImportFileSelect: 
         componentProc = ImportScrapbookFile; break;
      case kMovieImportSetSampleDurationSelect: 
         componentProc = ImportScrapbookSetSampleDuration; break;
   }
   if (componentProc)
      err = CallComponentFunctionWithStorage (storage, params,
                                              componentProc);
   return err;
}
pascal ComponentResult ImportScrapbookCanDo 
                           (ImportScrapbookGlobals storage, 
                              short ftnNumber)
{
   switch (ftnNumber) {
      case kComponentOpenSelect: 
      case kComponentCloseSelect: 
      case kComponentCanDoSelect: 
      case kComponentVersionSelect: 
      case kMovieImportFileSelect: 
      case kMovieImportSetSampleDurationSelect: 
         return true;
      default:
         return false;
   }
}
pascal ComponentResult ImportScrapbookVersion 
                              (ImportScrapbookGlobals storage)
{
   return 0x00010001;
}
pascal ComponentResult ImportScrapbookOpen 
                           (ImportScrapbookGlobals storage,
                            ComponentInstance self)
{
   storage = (ImportScrapbookGlobals) NewHandleClear 
                     (sizeof (ImportScrapbookGlobalsRecord));
   if (!storage) return MemError();
   (**storage).self = self;
   SetComponentInstanceStorage (self, (Handle)storage);
   return noErr;
}
pascal ComponentResult ImportScrapbookClose
                               (ImportScrapbookGlobals storage,
                                  ComponentInstance self)
{
   if (storage) DisposeHandle((Handle)storage);
   return noErr;
}

Importing a Scrapbook File

Before the import operation begins, the client may set the duration of samples to be added by the movie data import component by calling the MovieImportSetDuration function (described on page 9-27).

The MovieImportFile function (described on page 9-24) performs the import operation. The tasks involved in importing the data include

Listing 9-2 supplies an example in which a Scrapbook file is imported.

Listing 9-2 Importing a Scrapbook file

/* if this function is called, it provides a hint from the 
caller as to the desired sample (frame) duration in the new 
media */
pascal ComponentResult ImportScrapbookSetSampleDuration
                               (ImportScrapbookGlobals storage,
                                 TimeValue duration, 
                                 TimeScale scale)
{
   TimeRecord tr;
   tr.value.lo = duration;
   tr.value.hi = 0;
   tr.scale = 0;
   tr.base = nil;
   ConvertTimeScale (&tr, kMediaTimeScale);
               /* your new media will have a time scale of 600 */
   (**storage).frameDuration = tr.value.lo;
   return noErr;
}
pascal ComponentResult ImportScrapbookFile
                           (ImportScrapbookGlobals storage,
                            FSSpec *theFile, Movie theMovie,
                            Track targetTrack, Track *usedTrack,
                            TimeValue atTime, 
                            TimeValue *addedTime, 
                            long inFlags, long *outFlags)
{
   OSErr err;
   short resRef = 0, saveRes = CurResFile();
   PicHandle thePict;
   Rect trackRect;
   short pageIndex = 0;
   Track newTrack = 0;
   Media newMedia;
   Boolean endMediaEdits = false;
   TimeValue frameDuration;
   SampleDescriptionHandle sampleDesc = 0;
   *outFlags = 0;
   if (inFlags & movieImportMustUseTrack)
      return invalidTrack;
   /* open source file */
   resRef = FSpOpenResFile (theFile, fsRdPerm);
   if (err = ResError()) goto bail;
   UseResFile(resRef);
   /* get the first PICT to determine the track size */
   thePict = (PicHandle)Get1IndResource ('PICT', 1);
   if (!thePict) {
      err = ResError();
      goto bail;
   }
   trackRect = (**thePict).picFrame;
   OffsetRect(&trackRect, -trackRect.left, -trackRect.top);
   /* create a track and PICT media */
   newTrack = NewMovieTrack (theMovie, trackRect.right << 16,
                            trackRect.bottom << 16, kNoVolume);
   if (err = GetMoviesError()) goto bail;
   newMedia = NewTrackMedia (newTrack, 'PICT', kMediaTimeScale,
                            nil, 0);
   if (err = GetMoviesError()) goto bail;
   if (err = BeginMediaEdits (newMedia)) goto bail;
   endMediaEdits = true;
   /* determine the frame duration (check the hint you may 
      have been called with) */
   frameDuration = (**storage).frameDuration;
   if (!frameDuration) frameDuration = kMediaTimeScale/5;
                              /* default is 1/5th second */
   /* set up a simple sample description */
   sampleDesc = (SampleDescriptionHandle) NewHandleClear 
                                 (sizeof (SampleDescription));
   (**sampleDesc).descSize = sizeof(SampleDescription);
   (**sampleDesc).dataFormat = 'PICT';
   /* cycle through all source frames and add them to the media */
   do {
      Handle thePict; 
      short resID = pageToMapIndex (++pageIndex, 
                                    *GetResource ('SMAP', 0));
      if (resID == 0) break;
      thePict = Get1Resource ('PICT', resID);
      if (thePict == nil) continue; /* some pages may not 
                                       contain a 'PICT' */
      err = AddMediaSample(newMedia, thePict, 0, 
                           GetHandleSize (thePict),
                            frameDuration, sampleDesc, 1, 0, nil);
      ReleaseResource (thePict);
      DisposeHandle (thePict);
   } while (!err);
   if (err) goto bail;
   /* add the new media to the track */
   err = InsertMediaIntoTrack (newTrack, 0, 0, 
                              GetMediaDuration (newMedia), kFix1);
bail:
   if (resRef) CloseResFile (resRef);
   if (endMediaEdits) EndMediaEdits (newMedia);
   if (err && newTrack) {
      DisposeMovieTrack (newTrack);
      newTrack = 0;
   }
   UseResFile (saveRes);
   if (sampleDesc) DisposeHandle ((Handle)sampleDesc);
   *usedTrack = newTrack;
   return err;
}
/* map from a Scrapbook page number to a resource ID */
short pageToMapIndex (short page, Ptr map)
{
   short mapIndex;
   for (mapIndex = 0; mapIndex < 256; mapIndex++)
      if (*map++ == page)
         return mapIndex | 0x8000;
   return 0;
}

A Sample Movie Export Component

As with movie data import components, the movie data export component should not rely on any configuration information beyond that which is supplied by the Movie Toolbox when it calls the MovieExportToHandle or MovieExportToFile function (described on page 9-35 and page 9-36, respectively).

This section provides an implementation of a movie data exchange component that exports a movie or movie's track to a PICS animation file.

Implementing the Required Export Component Functions

Listing 9-3 provides the component dispatchers for the movie export component together with the required functions.

Listing 9-3 Implementing the required export functions

typedef struct {
   ComponentInstance self;
} ExportPICSGlobalsRecord, *ExportPICSGlobals;
/* entry point for all Component Manager requests */
pascal ComponentResult ExportPICSDispatcher 
                              (ComponentParameters *params,
                               Handle storage)
{
   OSErr err = badComponentSelector;
   ComponentFunction componentProc = 0;
   switch (params->what) {
      case kComponentOpenSelect: 
         componentProc = ExportPICSOpen; break;
      case kComponentCloseSelect: 
         componentProc = ExportPICSClose; break;
      case kComponentCanDoSelect: 
         componentProc = ExportPICSCanDo; break;
      case kComponentVersionSelect: 
         componentProc = ExportPICSVersion; break;
      case kMovieExportToFileSelect: 
         componentProc = ExportPICSToFile; break;
   }
   if (componentProc)
      err = CallComponentFunctionWithStorage (storage, params,
                                              componentProc);
   return err;
}
pascal ComponentResult ExportPICSCanDo (ExportPICSGlobals store,
                                        short ftnNumber)
{
   switch (ftnNumber) {
      case kComponentOpenSelect:
      case kComponentCloseSelect:
      case kComponentCanDoSelect:
      case kComponentVersionSelect:
      case kMovieExportToFileSelect:
         return true;
         break;
      default:
         return false;
         break;
   }
}
pascal ComponentResult ExportPICSVersion (ExportPICSGlobals store)
{
   return 0x00010001;
}
pascal ComponentResult ExportPICSOpen (ExportPICSGlobals store,
                                        ComponentInstance self)
{
   OSErr err;
   store = (ExportPICSGlobals) NewPtrClear 
            (sizeof(ExportPICSGlobalsRecord));
   if (err = MemError()) goto bail;
   store->self = self;
   SetComponentInstanceStorage(self, (Handle)store);
bail:
   return err;
}
pascal ComponentResult ExportPICSClose (ExportPICSGlobals store,
                                        ComponentInstance self)
{
   if (store) DisposPtr((Ptr)store);
   return noErr;
}

Exporting Data to a PICS File

To export data to a PICS file, your component must

Listing 9-4 provides an implementation of these tasks in a movie export component. The ExportPICSToFile function performs the export operation by opening the resource fork of the PICS file and cycling through the movie time segment, adding a frame to the PICS file.

Listing 9-4 Exporting a frame of movie data to a PICS file

pascal ComponentResult ExportPICSToFile (ExportPICSGlobals store, 
                                          const FSSpec *theFile, 
                                          Movie m, 
                                          Track onlyThisTrack,
                                          TimeValue startTime,
                                          TimeValue duration)
{
   OSErr err = noErr;
   short resRef = 0;
   short saveResRef = CurResFile();
   short resID = 128;
   PicHandle thePict = nil;
   /* open the resource fork of the PICS file 
      (the caller is responsible for creating the file) */
   resRef = FSpOpenResFile (theFile, fsRdWrPerm);
   if (err = ResError()) goto bail;
   UseResFile(resRef);
   /* cycle through the movie time segment you were given */
   while (startTime < duration) {
      Byte c = 0;
      if (onlyThisTrack)
         thePict = GetTrackPict (onlyThisTrack, startTime);
      else
         thePict = GetMoviePict(m, startTime);
      if (!thePict) continue;
      /* add a frame to the PICS file */
      AddResource ((Handle)thePict, 'PICT', resID++, &c);
      err = ResError();
      WriteResource ((Handle)thePict);
      DetachResource ((Handle)thePict);
      KillPicture (thePict);
      thePict = nil;
      if (err) break;
      /* find the time of the next frame */
      do {
         TimeValue nextTime;
         if (onlyThisTrack)
            GetTrackNextInterestingTime (onlyThisTrack,
                                  nextTimeMediaSample, startTime, 
                                  kFix1, &nextTime, nil);
         else {
            OSType mediaType = VisualMediaCharacteristic;
            GetMovieNextInterestingTime (m, nextTimeMediaSample, 
                                          1, &mediaType, 
                                          startTime, kFix1,
                                          &nextTime, nil);
         }
         if (GetMoviesError ()) goto bail;
         if (nextTime != startTime) {
            startTime = nextTime;
            break;
         }
      } while (++startTime < duration);
   }
bail:
   if (thePict) KillPicture (thePict);
   if (resRef) CloseResFile (resRef);
   UseResFile (saveResRef);
   return err;
}

Subtopics
A Sample Movie Import Component
Implementing the Required Import Component Functions
Importing a Scrapbook File
A Sample Movie Export Component
Implementing the Required Export Component Functions
Exporting Data to a PICS File

Previous Book Contents Book Index Next

© Apple Computer, Inc.
7 JUL 1996




Navigation graphic, see text links

Main | Top of Section | What's New | Apple Computer, Inc. | Find It | Feedback | Help